+function (exports) {
    "use strict";
    
    exports.OAuth2 = OAuth2;
    
    var developerKey = "AI39si54Gen8gisQllSDy60YarJLpvwHhuC3mlP3tUMhbYQ2_" +
            "L6J26-vjOQaIi736ezMnD4a-UTLeCxLVOsa7jyHD8eAroVNyw";
    
    /**
     * OAuth2 is a simple abstraction over OAuth2 API provided by Chrome.
     * 
     * @public
     * @constructor
     */
    function OAuth2() {
    }
    
    /**
     * Performs an OAuth2-authorized HTTP GET request.
     * 
     * @param {string} uri Request URI.
     * @param {Object<string, string>} params Query parameters.
     * @param {function(string)} success Callback to execute on a successful
     *        request.
     * @param {function(XMLHttpRequest|Error)} failure Callback to execute if
     *        the request fails.
     */
    OAuth2.prototype.get = function (uri, params, success, failure) {
        var encodedParams, param, fullUri;
        encodedParams = [];
        for (param in params) {
            if (params.hasOwnProperty(param)) {
                encodedParams.push(encodeURIComponent(param) + "=" +
                        encodeURIComponent(params[param]));
            }
        }
        fullUri = uri + "?" + encodedParams.join("&");
        this.request("GET", fullUri, {}, null, function (xhr) {
            if (xhr.status === 200) {
                success(xhr.responseText);
            } else if (failure) {
                failure(xhr);
            }
        });
    };
    
    /**
     * Performs an OAuth2-authorized HTTP POST request to the specified URI.
     * 
     * @param {string} uri Base URI without query parameters.
     * @param {Object<string, string>} params Query string parameters.
     * @param {string} contentType MIME content type of the request body.
     * @param {string} body Request body.
     * @param {function(string)} callback Callback to execute on success.
     * @param {function(Error|XMLHttpRequest)} errorCallback
     */
    OAuth2.prototype.post = function (uri, params, contentType, body, callback,
            errorCallback) {
        var encodedParams, param, fullUri, headers;
        encodedParams = [];
        for (param in params) {
            if (params.hasOwnProperty(param)) {
                encodedParams.push(encodeURIComponent(param) + "=" +
                        encodeURIComponent(params[param]));
            }
        }
        fullUri = uri + "?" + encodedParams.join("&");
        headers = {
            "GData-Version": "2",
            'X-GData-Key': 'key=' + developerKey,
            "Content-Type": contentType
        };
        this.request("POST", fullUri, headers, body, function (request) {
            if (request instanceof Error) {
                errorCallback(request);
            }
            if ((request.status >= 200) && (request.status < 300)) {
                callback(request.responseText);
            } else {
                errorCallback(request);
            }
        });
    };
    
    /**
     * Performs an OAuth2-authorized HTTP request.
     * 
     * @param {string} method HTTP method to use.
     * @param {string} uri Request URI.
     * @param {Object<string, string>} headers Additional headers to send.
     * @param {?string} content The content to send to the server.
     * @param {function(XMLHttpRequest)} callback Callback to execute when the
     *        request finishes.
     */
    OAuth2.prototype.request = function (method, uri, headers, content,
            callback) {
        this.getToken(function (accessToken) {
            var xhr, header;
            if (!accessToken) {
                callback(new Error("Cannot obtain OAuth2 token"));
                return;
            }
            xhr = new XMLHttpRequest();
            xhr.open(method, uri, true);
            xhr.setRequestHeader("Authorization", "Bearer " + accessToken);
            for (header in headers) {
                if (headers.hasOwnProperty(header)) {
                    xhr.setRequestHeader(header, headers[header]);
                }
            }
            xhr.onreadystatechange = function () {
                if (xhr.readyState !== 4) {
                    return;
                }
                callback(xhr);
            };
            xhr.send(content);
        });
    };
    
    /**
     * Generates a OAuth2 token. The method prompts the user automatically if
     * neccessary.
     * 
     * @param {function(string)} callback The callback to execute once a token
     *        has been generated.
     */
    OAuth2.prototype.getToken = function (callback) {
        chrome.identity.getAuthToken({
            interactive: true
        }, callback);
    };
    
    /**
     * Requests the OAuth2 authorization using the native Chrome API.
     * 
     * @param {function(string)} success Callback to execute on success. The
     *        callback will receive the generated authorization token that was
     *        created in the process.
     * @param {function(string)} failure Callback to execute on failure. The
     *        callback will receive the error message provided by Chrome (if
     *        any).
     */
    OAuth2.prototype.requestAuthorization = function (success, failure) {
        chrome.identity.getAuthToken({
            interactive: false
        }, function (token) {
            if (token) {
                success(token);
            } else {
                failure(chrome.runtime.lastError);
            }
        });
    };
}(window);
